/**
 * Command 命令模式
 * 定义：将一个请求封装为一个对象，从而使你可用不同的请求对客户进行参数化；对请求排队或记录请求日志，以及支持可撤销的操作。
 * 要点：
1)Command模式的根本目的在于将“行为请求者”与“行为实现者” 解耦，在面向对象语言中，常见的实现手段是“将行为抽象为对象”。
2)实现Command接口的具体命令对象ConcreteCommand有时候根据需要可能会保存一些额外的状态信息。
3)通过使用Composite模式，可以将多个“命令”封装为一个“复合命令”MacroCommand。
4)Command模式与C#中的Delegate有些类似。但两者定义行为接口的规范有所区别：Command以面向对象中的“接口-实现”来定义行为接口规范，
更严格，更符合抽象原则；Delegate以函数签名来定义行为接口规范，更灵活，但抽象能力比较弱。
 */

 
// 命令模式 -------------
// 示例1
class Command {//每一个命令都封装成一个类
  execute() {
    // 由子类实现
  }
}
class DeleteCommand extends Command {
  execute() {
    console.log("删除")
  }
}
class AddCommand extends Command {
  execute() {
    console.log("添加")
  }
}
class EditCommand extends Command {
  execute() {
    console.log("编辑")
  }
}
class MenuItem {
  constructor(text, command) {
    this.text = text
    this.command = command
  }
  click() {
    this.command.execute()
  }
}

var deleteMenu = new MenuItem("删除", new DeleteCommand())
var addMenu = new MenuItem("新增", new AddCommand())
var editMenu = new MenuItem("编辑", new EditCommand())
deleteMenu.click()


// 示例2
let pos = {
  x: 0,
  y: 0
}
class Command {
  constructor(commandName) {
    this.commandName = commandName
  }
  execute() {
    // 由子类实现
  }
  canExecute(commandName) {
    return this.commandName == commandName
  }
}
class UpCommand extends Command {
  constructor() {
    super('up')
  }
  execute() {
    pos.y -= 1
    console.log("上", pos.x + ' | ' + pos.y)
  }

}
class DownCommand extends Command {
  constructor() {
    super('down')
  }
  execute() {
    pos.y += 1
    console.log("下", pos.x + ' | ' + pos.y)
  }
}
class LeftCommand extends Command {
  constructor() {
    super('left')
  }
  execute() {
    pos.x -= 1
    console.log("左", pos.x + ' | ' + pos.y)
  }
}
class RightCommand extends Command {
  constructor() {
    super('right')
  }
  execute() {
    pos.x += 1
    console.log("右", pos.x + ' | ' + pos.y)
  }
}
const commandManager = (function () {
  let commands = [];

  function addCommand(command) {
    commands.push(command)
  }

  function invoke(commandName) {
    commands.forEach(m => {
      if (m.canExecute(commandName)) {
        m.execute()
      }
    })
  }
  return {
    addCommand,
    invoke
  }
})()
commandManager.addCommand(new UpCommand())
commandManager.addCommand(new DownCommand())
commandManager.addCommand(new LeftCommand())
commandManager.addCommand(new RightCommand())
commandManager.invoke('up')

// 示例三，redux也采用了命令模式思想
class Reducer {
  constructor(action) {
    this.action = action
  }
  execute() {
    // 由子类实现
  }
  canExecute(action) {
    return this.action == action
  }
}
class DeleteReducer extends Reducer {
  constructor() {
    super('delete')
  }
  execute() {
    console.log("删除")
  }

}
class AddReducer extends Reducer {
  constructor() {
    super('add')
  }
  execute() {
    console.log("添加")
  }
}
class EditReducer extends Reducer {
  constructor() {
    super('edit')
  }
  execute() {
    console.log("编辑")
  }
}

const store = (function () {
  let reducers = [];

  function addReducer(reducer) {
    reducers.push(reducer)
  }

  function dispatch(action) {
    reducers.forEach(m => {
      if (m.canExecute(action)) {
        m.execute()
      }
    })
  }
  return {
    addReducer,
    dispatch
  }
})()
store.addReducer(new DeleteReducer())
store.addReducer(new AddReducer())
store.addReducer(new EditReducer())
store.dispatch('add')